﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenFileTransfer.Util.Json;
using OpenFileTransfer.Util.Threading;
using OpenFileTransfer.Wpf.Models;
using OpenFileTransfer.Wpf.Repositories;

namespace OpenFileTransfer.Wpf.Services
{
    public class CommunicationService : ICommunicationService
    {
        public event Action DeviceChanged = () => { };

        public List<UdpDevice> UdpDeviceList { get; }

        private readonly AsyncWorkHelper _heartbeatHelper;
        private readonly AsyncWorkHelper _broadcastHelper;
        private readonly int _broadcastPort;
        private readonly byte[] _broadcastBuffer;

        private readonly string _localDeviceId;

        private readonly ILogService _logService;
        private readonly IConfigService _configService;
        private readonly IUdpService _udpService;

        public CommunicationService(
            ILogService logService,
            IConfigService configService,
            IUdpService udpService,
            IRepository<DeviceInfoModel> deviceInfoRepository
        )
        {
            _logService = logService;
            _configService = configService;
            _udpService = udpService;
            _broadcastPort = configService.UdpPort;

            UdpDeviceList = new List<UdpDevice>();

            _udpService.DataReceived += OnDataReceived;

            //检查本地设备Id
            _localDeviceId = GetLocalDeviceIdAsync(deviceInfoRepository).GetAwaiter().GetResult();
            var packet = new UdpServerPacket
            {
                DeviceId = _localDeviceId,
                DisplayName = Environment.MachineName
            };
            var info = JsonHelper.SerializeObject(packet);
            _broadcastBuffer = Encoding.UTF8.GetBytes(info);

            var interval = configService.UdpBroadcastInterval * 1000;
            _broadcastHelper = new AsyncWorkHelper(BroadcastAsync, interval);
            _heartbeatHelper = new AsyncWorkHelper(HeartbeatAsync, 1000);
        }

        private static async Task<string> GetLocalDeviceIdAsync(IRepository<DeviceInfoModel> repository)
        {
            var deviceList = await repository.GetAsync(t => t.IsSelf == 1);
            if (deviceList.Any())
            {
                return deviceList.First().Id ?? string.Empty;
            }

            var now = DateTime.Now;
            var model = new DeviceInfoModel()
            {
                Id = $"{Guid.NewGuid()}".Replace("-", ""),
                DisplayName = Environment.MachineName,
                RemarkName = Environment.MachineName,
                IsSelf = 1,
                CreateTime = now,
                UpdateTime = now
            };
            repository.Add(model);
            await repository.SaveAsync();

            return model.Id;
        }

        public void Startup()
        {
            _broadcastHelper.Start();
            _heartbeatHelper.Start();
        }

        public void Shutdown()
        {
            _broadcastHelper.Stop();
            _heartbeatHelper.Stop();
        }

        private async Task BroadcastAsync()
        {
            try
            {
                await _udpService.BroadcastAsync(_broadcastPort, _broadcastBuffer);
            }
            catch (Exception e)
            {
                _logService.Error($"广播消息异常: {e.Message}");
            }
        }

        private async Task HeartbeatAsync()
        {
            await Task.CompletedTask;

            var items = UdpDeviceList
                .Where(t =>
                    (DateTime.Now - t.UpdateTime).TotalMilliseconds >=
                    _configService.UdpBroadcastInterval * 2000)
                .ToList();
            items.ForEach(t => UdpDeviceList.Remove(t));
            if (items.Any())
            {
                DeviceChanged.Invoke();
            }
        }

        private void OnDataReceived(string ip, int port, byte[] data)
        {
            if (port != _broadcastPort)
            {
                return;
            }

            UdpServerPacket packet;
            try
            {
                var info = Encoding.UTF8.GetString(data);
                packet = JsonHelper.DeserializeObject<UdpServerPacket>(info);
            }
            catch (Exception)
            {
                //Do nothing
                return;
            }

            if (packet == null) // || packet.DeviceId == _localDeviceId)
            {
                return;
            }

            var device = UdpDeviceList.FirstOrDefault(t => t.DeviceId == packet.DeviceId);
            if (device != null)
            {
                device.UpdateTime = DateTime.Now;
            }
            else
            {
                device = new UdpDevice()
                {
                    DeviceId = packet.DeviceId,
                    DisplayName = packet.DisplayName,
                    UpdateTime = DateTime.Now
                };
                UdpDeviceList.Add(device);
                DeviceChanged.Invoke();
            }
        }
    }
}
